package pathways;

/**
 * Title:        Pathways
 * Description:  Pure Java version of the Pathways Project.
 * Copyright:    Copyright (c) 2002
 * Company:      Ants Technologies
 * @author Veejay Sani
 * @version 1.0
 */

import java.awt.Component;
import java.awt.Graphics;
import java.awt.Rectangle;
import java.sql.ResultSet;
import java.sql.SQLException;
import java.util.LinkedList;
import java.util.Vector;

import javax.swing.JFrame;
import javax.swing.JPanel;

import pathways.Database.Implementation.CDatabase;
import pathways.SpeciesSpace.CInstantiation;
import pathways.util.CDBUtil;
import pathways.util.CUtil;
import pathways.util.Debug.CDebug;

public class CPathway extends JPanel{
    
  public CPathway(CDatabase database)
  {
    reactions = new Vector();
    substrates = new Vector();
    substratesAsEnzymes = new Vector();
    painter = null;
    substrateMouseListener = null;
    enzymeMouseListener = null;
    guiMouseListener = new CMouseListener();
    
    addMouseListener(guiMouseListener);
    addMouseMotionListener(guiMouseListener);
    
    db = database;
  }

  public void loadLinearPathwayFromVector(Vector rxns)
  {
    // create the rxn
    // read the rxn
    
    this.disposeAndReCreate();
    
    Rectangle rect = new Rectangle(0,10,CMain.RECTWIDTH,CMain.RECTHEIGHT);
    CSubstrate sub = new CSubstrate("",0, rect);
    CSubstrate prod = null;
    CEnzyme enz = null;
    CReaction rxn = null;
    Integer rxnID = null;
    
    for (int i = 0; i < rxns.size(); i++)
    {
      rxnID = (Integer)rxns.get(i);
      
      rect = getNextRect(new Rectangle(rect),2);
      prod = new CSubstrate("",0, rect);
      enz = new CEnzyme("",0);
      
      rxn = new CReaction(sub,enz,prod,2,0,rxnID.intValue());
      
      rxn.read(rxnID.intValue(),db);
      
      addReaction(rxn);
      
      sub = prod;
    }
  }
  
  private Rectangle getNextRect(Rectangle r, int connectFrom)
  {
    Rectangle rect = new Rectangle(0,0,0,0);
    
    if (connectFrom == 0)
    {
      rect = new Rectangle(r.x-2*r.width, r.y, r.width,r.height);
    }
    else if (connectFrom == 1)
    {
      rect = new Rectangle(r.x, r.y-2*r.height, r.width,r.height);
    }
    else if (connectFrom == 2)
    {
      rect = new Rectangle(r.x+2*r.width, r.y, r.width,r.height);
    }
    else if (connectFrom == 3)
    {
      rect = new Rectangle(r.x, r.y+2*r.height, r.width,r.height);
    }
    else
    {
      // raise exception
    }

    return rect;
  }
  
  public Component getGUIComponent()
  {
    return this;
  }

  public IPathwayPainter getPathwayPainter()
  {
    return painter;
  }

  public void setPainter(IPathwayPainter p)
  {
    painter = p;
  }

  public void addReaction(CReaction rxn)
  {
    adjustReactionIfLoop(rxn);
    reactions.add(rxn);
    addRxnToSubstrateList(rxn);
  }
  
  private void adjustReactionIfLoop(CReaction rxn)
  {
    CSubstrate product = rxn.getProduct();
    CSubstrate sub = null;
    LinkedList list = null;
    
    for (int i = 0; i < substrates.size(); i++)
    {
       list = (LinkedList)substrates.get(i);
       sub = (CSubstrate)list.getFirst();
       
       if (sub.getID() == product.getID())
       {
          // set prod of reaction to the equivalent found
          rxn.setProduct(sub);
          
          return; 
       }   
    }  
  }
  
  private void addRxnToSubstrateList(CReaction rxn)
  {
    CSubstrate substrate = rxn.getSubstrate();
    CSubstrate product = rxn.getProduct();
    CEnzyme enzyme = rxn.getEnzyme();

    substrate.addMouseListener(substrateMouseListener);
    product.addMouseListener(substrateMouseListener);
    enzyme.addMouseListener(enzymeMouseListener);

    // creates a list if not found
    LinkedList subList = getOrCreateListForSubstrate(substrate);
    LinkedList prodList = getOrCreateListForSubstrate(product);

    subList.add(product);
  }

  public void draw(IPathwayPainter p, Graphics g)
  {
    beforeDraw();
    p.draw(g,this);
    redoGUIMouseListener();
  }
          
  public void draw()
  {
    if (painter != null)
    {
      this.repaint();
      //????????????????????draw(painter,gui.getGraphics());
    }
    else
    {
      // raise exception
    }
  }
  

  public Vector getReactions()
  {
    return reactions;
  }

  public Vector getSubstrateList()
  {
    return substrates;
  }

  public CSubstrate getSubstrateFromList(int id)
  {
    LinkedList list = null;
    CSubstrate sub = null;
    
    for (int i = 0; i < substrates.size(); i++)
    {
      list = (LinkedList)substrates.get(i);
      sub = (CSubstrate)list.getFirst();

      if (sub.getID() == id)
        return sub;
    }
    
    return null;
  }
  
  private LinkedList getOrCreateListForSubstrate(CSubstrate s)
  {
    LinkedList list = null;
    CSubstrate sub = null;
    boolean found = false;

    for (int i = 0; i < substrates.size() && (!found); i++)
    {
      list = (LinkedList)substrates.get(i);
      sub = (CSubstrate)list.getFirst();

      if (sub == s)
        found = true;
    }

    if (!found)
    {
      list = new LinkedList();
      list.add(s);
      substrates.add(list);
    }

    return list;
  }

  public void setSubstrateMouseListener(ISubstrateMouseListener listener)
  {
    LinkedList list = null;
    CSubstrate sub = null;

    for (int i = 0; i < substrates.size(); i++)
    {
      list = (LinkedList)substrates.get(i);
      sub = (CSubstrate)list.getFirst();

      sub.addMouseListener(listener);
    }

    substrateMouseListener = listener;
  }

  private void redoGUIMouseListener()
  {
    LinkedList list = null;
    CSubstrate sub = null;

    guiMouseListener.getItems().clear();

    for (int i = 0; i < substrates.size(); i++)
    {
      list = (LinkedList)substrates.get(i);
      sub = (CSubstrate)list.getFirst();

      guiMouseListener.addItem(sub);
    }

    CReaction rxn = null;

    for (int i = 0; i < reactions.size(); i++)
    {
      rxn = (CReaction)reactions.get(i);
      guiMouseListener.addItem(rxn.getEnzyme());
    }

  }

  public void setEnzymeMouseListener(IEnzymeMouseListener enzymelistener)
  {
    enzymeMouseListener = enzymelistener;
  }

  public void paintComponent(Graphics g)
  {
    super.paintComponent(g);
    
    draw(painter,g);
  }
  
  public CDatabase getDatabase()
  {
    return db;  
  }
  
  public void disposeAndReCreate()
  {
    reactions = new Vector();
    substrates = new Vector();
    substratesAsEnzymes = new Vector();  
  }
  
  public CReaction getReactionForEnzyme(CEnzyme e)
  {
    CReaction rxn = null;
    
    for (int i = 0; i < reactions.size(); i++)
    {
      rxn = (CReaction)reactions.get(i);
      if (rxn.getEnzyme() == e)
      {
        return rxn; 
      }
    }
    
    return null;   
  }

  public CReaction getReactionFromList(int rxnID)
  {
    CReaction rxn = null;
    
    for (int i = 0; i < reactions.size(); i++)
    {
      rxn = (CReaction)reactions.get(i);
      if (rxn.getID() == rxnID)
      {
        return rxn; 
      }
    }
    
    return null;   
  }

  public CReaction getReactionFromList(int rxnID, int subID)
  {
    // the subID is needed when the pathway has more than one
    // missing info link or telescopes which have an RxnID = 0
    // in that one can get the specific rxn by using the substrate
    // of the reaction
    
    CReaction rxn = null;
    
    for (int i = 0; i < reactions.size(); i++)
    {
      rxn = (CReaction)reactions.get(i);
      if (rxn.getID() == rxnID && rxn.getSubstrate().getID() == subID)
      {
        return rxn; 
      }
    }
    
    return null;   
  }
  
  public void setOwner(JFrame frame)
  {
    owner = frame;  
  }
  
  public JFrame getOwner()
  {
    return owner;  
  }
  
  public Vector getSubstratesAsEnzymes()
  {
    return substratesAsEnzymes;
  }
  
  public void beforeDraw()
  {
    for (int i = 0; i < substratesAsEnzymes.size(); i++)
    {
      CSubstrateAsEnzyme subAsEnzyme = (CSubstrateAsEnzyme)substratesAsEnzymes.get(i);
      subAsEnzyme.calcPoint2();
    }  
  }
  
  public boolean save(int userID, String pathwayName)
  {
    // save reactions
    CReaction rxn = null;
    int pathwayID = 0;
        
    pathwayID = db.getUpdate().newPathway(userID,pathwayName,CUtil.getCurrentTime());
    
    if (pathwayID == 0)
       return false;
    
    for (int i = 0; i < reactions.size(); i++)
    {
      rxn = (CReaction)reactions.get(i);
      
      if (!saveReaction(pathwayID,rxn))
         return false;
    }
    
    if (!saveSubstratesAsEnzymes(pathwayID))
       return false;
     
    if (!saveConnectionVisibleBranches(pathwayID))
       return false;
    
    return true;      
  }
  
  private boolean saveReaction(int pathwayID, CReaction rxn)
  {
    return db.getUpdate().newSavedPathwayItem(pathwayID,
           (int)rxn.getID(),
           (int)rxn.getSubstrate().getID(),
           (int)rxn.getProduct().getID(),
           CUtil.getString(rxn.getSubstrate().getRect()),
           CDBUtil.COORDINATE_TYPE_RECTANGULAR,
           CUtil.getString(rxn.getProduct().getRect()), 
           CDBUtil.COORDINATE_TYPE_RECTANGULAR,
           rxn.getFromPoint(),
           rxn.getToPoint(),
           rxn.getInstantiationID());
  }
  
  public void loadPathway(CPathway p)
  {
    this.disposeAndReCreate();
    
    reactions = p.reactions;
    substrates = p.substrates;
    substratesAsEnzymes = p.substratesAsEnzymes;
  }
  
  public boolean loadPathway(int pathwayID)
  {
    ResultSet rs = db.getSelect().getPathway(pathwayID);
    
    this.disposeAndReCreate();
    
    try {
      while(rs.next())
      {
        loadReaction(rs);   
      }  
    } catch (SQLException sqle) {
      CDebug.println(this,"loadPathway() SQLException: " + sqle.toString());
      return false;
    }
    
    if (!loadSubstratesAsEnzymes(pathwayID))
       return false;
    
    if (!loadConnectionBranches(pathwayID))
       return false;
    
    return true;
  } 
  
  private boolean loadReaction(ResultSet rs) throws SQLException
  {
    // Pre: the cursor of rs is on a valid record
    int rxnID = rs.getInt(3);
    int subID = rs.getInt(4);
    int prodID = rs.getInt(5);
    String subsCoords = rs.getString(6);
    String prodCoords = rs.getString(8);
    int from = rs.getInt(10);
    int to = rs.getInt(11);
    int instRxnID = rs.getInt(12);
    Rectangle subRect = CUtil.getRect(subsCoords);
    Rectangle prodRect = CUtil.getRect(prodCoords);
    
    // try to find the substrate if it has already been added
    // this will ensure that the one substrate is only laid out
    // once on the screen
    // If it is not found then we will will read it from the database
    // this will allow the missing link case to work which has an 
    // rxnID = 0 which makes the rxn.read() call to fail
    // but since we already read the substrate & prodcut and 
    // put them in a CReaction object then we have preety much
    // created a missing info link (rxnID = 0) so the missing link
    // case will work
    CSubstrate sub = this.getSubstrateFromList(subID);
    
    if (sub == null)
    {
       sub = new CSubstrate("",0,subRect);
       sub.read(subID,db);
    }
    
    CSubstrate prod = this.getSubstrateFromList(prodID);
    
    if (prod == null)
    {
       prod = new CSubstrate("",0,prodRect);
       prod.read(prodID,db);
    }
    
    // note that in the case of missing link 
    // enzymeID = 0 and RxnID = 0
    // So this will allow missing link to work as well
    CReaction rxn = new CReaction(sub,new CEnzyme("",0),prod,from,to,(long)rxnID);
    
    rxn.read((long)rxnID,db);
    
    if (instRxnID > 0)
    {
      CInstantiation inst = new CInstantiation(db);
      inst.read(instRxnID,db);
      rxn.setInstantiation(inst);
    }
    
    this.addReaction(rxn);
    
    return true;
  }
  
  private boolean saveConnectionVisibleBranches(int pathwayID)
  {
    // the purpose of the function is to save
    // the branches that are visible on any connection
    // point that has more than one branch on it
    LinkedList list = null;
    CSubstrate sub = null;

    for (int i = 0; i < substrates.size(); i++)
    {
      list = (LinkedList)substrates.get(i);
      sub = (CSubstrate)list.getFirst();

      for (int j = 0; j < 4; j++)
      {
        CConnectionPoint p = sub.getConnectionPoint(j);
        
        if (p.getFirstReactionAtEachBranch().size() > 1)
        {
          // save the visible branch
          CReaction rxn = p.getAttachedReaction();
          int rxnID = 0;
          
          if (rxn != null)
             rxnID = (int)rxn.getID();
          else
             rxnID = -1000;  // will not find - i.e. no reaction attached 
          
          if (!db.getUpdate().newPathwayConnectionPointBranch(pathwayID,(int)sub.getID(),j,rxnID))
             return false;
        } 
      }
    }   
        
    return true;  
  }
  
  private boolean loadConnectionBranches(int pathwayID)
  {
    // Pre: Must be called after all the substrates and reactions
    // have been loaded into the pathway
    ResultSet rs = db.getSelect().getPathwayConnectionPointBranches(pathwayID);
    
    try {
      while(rs.next())
      {
        int subID = rs.getInt(1);
        int connPoint = rs.getInt(2);
        int rxnID = rs.getInt(3); // reaction that should be connected at connection point
        
        CSubstrate sub = this.getSubstrateFromList(subID);  
        
        if (sub == null)
        {
          CDebug.println(this, "loadConnectionBranches() - Substrate not found in list");
          return false;
        }
        
        CConnectionPoint p = sub.getConnectionPoint(connPoint);
        
        if (p == null)
        {
          CDebug.println(this, "loadConnectionBranches() - Invalid Connection Point: " + connPoint);
          return false; 
        }
        
        CReaction rxn = this.getReactionFromList(rxnID, subID);
        
        // rxnID = -1000 means no reaction connected and then we detachall
        if (rxn == null && rxnID != -1000)
        {
          CDebug.println(this, "loadConnectionBranches() - Reaction not found in list - RxnID: " + rxnID);
          return false; 
        }
        
        // now that we got everything so lets set the visible branch
        if (rxnID == -1000)
        {
          p.detachAll(null); 
        }
        else
        {
          int branchNum = p.getBranchNumForHeadReaction(rxn);
          
          if (branchNum >= 0)
          {
            p.detachAll2();
            p.attachBranch(branchNum); 
          }
          else
          {
            CDebug.println(this, "loadConnectionBranches() - Rxn not found in the connection point");
            return false; 
          }
        }
      }
    } catch (SQLException sqle) {
      CDebug.println(this, "loadConnectionBranches() - SQLException: " + sqle.toString());
      return false;
    }
    
    return true;  
  }
  
  private boolean saveSubstratesAsEnzymes(int pathwayID)
  {
    CSubstrateAsEnzyme subAsEnz = null;
    
    for (int i = 0; i < substratesAsEnzymes.size(); i++)
    {
      subAsEnz = (CSubstrateAsEnzyme)substratesAsEnzymes.get(i);
      
      CReaction rxn = this.getReactionForEnzyme(subAsEnz.getEnzyme());
      CSubstrate sub = subAsEnz.getSubstrate();
      
      if (!db.getUpdate().newPathwaySubstrateAsEnzyme(pathwayID,(int)sub.getID(),(int)rxn.getID(),subAsEnz.getConnectingFrom(),subAsEnz.getConnectingTo()))
         return false;
    }
    
    return true;
  }
  
  private boolean loadSubstratesAsEnzymes(int pathwayID)
  {
    ResultSet rs = db.getSelect().getPathwaySubstratesAsEnzymes(pathwayID);
    
    try {
      while (rs.next())
      {
        int subID = rs.getInt(1);
        int rxnID = rs.getInt(2);
        int fromPoint = rs.getInt(3);
        int toPoint = rs.getInt(4);
        
        CSubstrate sub = this.getSubstrateFromList(subID);
        CReaction rxn = this.getReactionFromList(rxnID);
        
        CSubstrateAsEnzyme subAsEnz = new CSubstrateAsEnzyme(sub,rxn.getEnzyme(),fromPoint,sub.getCoOrdinates().getPoint(fromPoint),toPoint);
        
        rxn.setSubAsEnzyme(subAsEnz);
        
        CConnectionPoint p = sub.getConnectionPoint(fromPoint); 
        Vector v = p.getFirstReactionAtEachBranch();
        v.add(rxn);
        p.attachBranch(v.size()-1);
        
        this.getSubstratesAsEnzymes().add(subAsEnz);
      }
    } catch(SQLException sqle) {
      CDebug.println(this, "loadSubstratesAsEnzymes() SQLException: " + sqle.toString());
      return false;
    }
    
    return true; 
  }
  
  private Vector reactions;
  private Vector substrates;
  private IPathwayPainter painter;
  private ISubstrateMouseListener substrateMouseListener;
  private CMouseListener guiMouseListener;
  private IEnzymeMouseListener enzymeMouseListener;
  private CDatabase db;
  private JFrame owner = null;
  private Vector substratesAsEnzymes;
}